/*
 * @文件描述: TFT驱动程序，ILI9806G,加上lvgl的一些初始化
 * @版本: 
 * @作者: 周晨阳
 * @Date: 2021-01-14 08:26:53
 */

#include "TFT.h"
#ifndef WIN_SIM
#include <stdio.h>
#include "GUI.h"
#include <rtthread.h>
#include "ff.h"
#include "delay.h"
//根据液晶扫描方向而变化的XY像素宽度
//调用TFT_GramScan函数设置方向时会自动更改
uint16_t LCD_X_LENGTH = TFT_MORE_PIXEL;
uint16_t LCD_Y_LENGTH = TFT_LESS_PIXEL;
#ifdef GUI_LVGL
#include "TOUCH.h"
#include "lv_hal_disp.h"
#include "lv_hal_indev.h"
#include "lv_theme.h"
#include "myFlash.h"
#include "fontupd.h"
#ifndef MONITOR_HOR_RES
#define MONITOR_HOR_RES TFT_MORE_PIXEL
#endif

#ifndef MONITOR_VER_RES
#define MONITOR_VER_RES TFT_LESS_PIXEL
#endif

#endif
//液晶屏扫描模式，本变量主要用于方便选择触摸屏的计算参数
//参数可选值为0-7
//调用TFT_GramScan函数设置方向时会自动更改
//LCD刚初始化完成时会使用本默认值
//5为横屏，6为竖屏
#define LCD_SCAN_MODE 5
uint8_t lcdScanMode = LCD_SCAN_MODE;

static uint16_t CurrentTextColor = BLACK; //前景色
static uint16_t CurrentBackColor = WHITE; //背景色

static lv_disp_drv_t disp_drv;
static uint32_t DMA_TotalCnt;
static uint8_t DMA_Trans_time;
__inline static void WriteData(u16 tem_data);
__inline static void WriteComm(u16 CMD);
__inline uint16_t TFT_Read_Data(void);
static uint16_t TFT_Read_PixelData(void);
static void LCD_REG_Config(void);
static void LCD_WR_REG(u16 Index, u16 CongfigTemp);

static void TFT_SetCursor(uint16_t usX, uint16_t usY);
static void LCD_Rst(void);
static __inline void TFT_FillColor(uint32_t ulAmout_Point, uint16_t usColor);

static void LCD_DMA_Init_Core(void);
static void LCD_Start_DMA_Fill(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t *color);
typedef struct
{
  uint16_t min;
  uint16_t max;
  uint8_t bpp;
  uint8_t reserved[3];
} x_header_t;
typedef struct
{
  uint32_t pos;
} x_table_t;
typedef struct
{
  uint8_t adv_w;
  uint8_t box_w;
  uint8_t box_h;
  int8_t ofs_x;
  int8_t ofs_y;
  uint8_t r;
} glyph_dsc_t;

static x_header_t __g_xbf_hd = {
    .min = 0x0020,
    .max = 0x9fa0,
    .bpp = 2,
};

static const uint8_t *__user_font_get_bitmap(const lv_font_t *font, uint32_t unicode_letter);
static bool __user_font_get_glyph_dsc(const lv_font_t *font, lv_font_glyph_dsc_t *dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next);
static uint8_t *__user_font_getdata(int offset, int size);
//YouYuan,,-1
//字模高度：18
//XBF字体,外部bin文件
const lv_font_t myFont = {
    .get_glyph_bitmap = __user_font_get_bitmap,
    .get_glyph_dsc = __user_font_get_glyph_dsc,
    .line_height = 18,
    .base_line = 0,
};

/**
 * @description: 写数据函数
 * @param {*}
 * @return {*}
 */
__inline static void WriteData(u16 tem_data)
{
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = tem_data;
}
/**
 * @description: 写命令函数
 * @param {*}
 * @return {*}
 */
__inline static void WriteComm(u16 CMD)
{
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = CMD;
}
/**
 * @description: 简易延时
 * @param {*}
 * @return {*}
 */
static void Delay(__IO u32 nCount)
{
  int i;
  for (i = 0; i < 0X7200; i++)
    for (; nCount != 0; nCount--)
      ;
}
/**
 * @brief  在TFT显示器上以某一颜色填充像素点
 * @param  ulAmout_Point ：要填充颜色的像素点的总数目
 * @param  usColor ：颜色
 * @retval 无
 */
static __inline void TFT_FillColor(uint32_t ulAmout_Point, uint16_t usColor)
{
  uint32_t i = 0;
  //原版
  /* memory write */
  // WriteComm(CMD_SetPixel);

  // for (i = 0; i < ulAmout_Point; i++)
  //   WriteData(usColor);

  //优化版
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) =
      (CMD_SetPixel);

  for (i = 0; i < ulAmout_Point; i++)
    *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usColor);
}
/**
 * @brief : LCD的FSMC初始化，不需要显式调用，已经由系统调用
 * @param {*}
 * @return {*}
 */
void LCD_FSMC_Init(void)
{
  FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
  FSMC_NORSRAMTimingInitTypeDef readWriteTiming;

  /* 使能FSMC时钟*/
  RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);

  //地址建立时间（ADDSET）为1个HCLK 5/168M=30ns
  readWriteTiming.FSMC_AddressSetupTime = 0x00; //地址建立时间
  //数据保持时间（DATAST）+ 1个HCLK = 12/168M=72ns
  readWriteTiming.FSMC_DataSetupTime = 0; //数据建立时间
  //选择控制的模式
  //模式B,异步NOR FLASH模式，与TFT的8080时序匹配
  readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;

  /*以下配置与模式B无关*/
  //地址保持时间（ADDHLD）模式A未用到
  readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间
  //设置总线转换周期，仅用于复用模式的NOR操作
  readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
  //设置时钟分频，仅用于同步类型的存储器
  readWriteTiming.FSMC_CLKDivision = 0x00;
  //数据保持时间，仅用于同步型的NOR
  readWriteTiming.FSMC_DataLatency = 0x00;

  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAMx;
  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
  FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;
  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &readWriteTiming;

  FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);

  /* 使能 FSMC_Bank1_NORSRAM3 */
  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAMx, ENABLE);
}
/*
 * 函数名：LCD_GPIO_Config
 * 描述  ：根据FSMC配置LCD的I/O
 * 输入  ：无
 * 输出  ：无
 * 调用  ：内部调用
 * 如果外部SRAM在TFT之前被初始化，则注释掉的引脚即为已经被初始化过了的
 */
void LCD_GPIO_Config(void)
{

  GPIO_InitTypeDef GPIO_InitStructure;

  // RCC_AHB1PeriphClockCmd(

  //     TFT_CS_CLK | TFT_DC_CLK | TFT_WR_CLK | TFT_RD_CLK | TFT_BK_CLK |
  //         TFT_RST_CLK | TFT_D0_CLK | TFT_D1_CLK | TFT_D2_CLK | TFT_D3_CLK |
  //         TFT_D4_CLK | TFT_D5_CLK | TFT_D6_CLK | TFT_D7_CLK | TFT_D8_CLK |
  //         TFT_D9_CLK | TFT_D10_CLK | TFT_D11_CLK | TFT_D12_CLK | TFT_D13_CLK |
  //         TFT_D14_CLK | TFT_D15_CLK,

  //     ENABLE);

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉

  // GPIO_InitStructure.GPIO_Pin = TFT_D0_PIN;
  // GPIO_Init(TFT_D0_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D1_PIN;
  // GPIO_Init(TFT_D1_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D2_PIN;
  // GPIO_Init(TFT_D2_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D3_PIN;
  // GPIO_Init(TFT_D3_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D4_PIN;
  // GPIO_Init(TFT_D4_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D5_PIN;
  // GPIO_Init(TFT_D5_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D6_PIN;
  // GPIO_Init(TFT_D6_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D7_PIN;
  // GPIO_Init(TFT_D7_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D8_PIN;
  // GPIO_Init(TFT_D8_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D9_PIN;
  // GPIO_Init(TFT_D9_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D10_PIN;
  // GPIO_Init(TFT_D10_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D11_PIN;
  // GPIO_Init(TFT_D11_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D12_PIN;
  // GPIO_Init(TFT_D12_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D13_PIN;
  // GPIO_Init(TFT_D13_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D14_PIN;
  // GPIO_Init(TFT_D14_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_D15_PIN;
  // GPIO_Init(TFT_D15_PORT, &GPIO_InitStructure);
  // /*
  //  * FSMC_NOE   :LCD-RD
  //  * FSMC_NWE   :LCD-WR
  //  * FSMC_NE1   :LCD-CS
  //  * FSMC_A16  	:LCD-DC
  //  */
  // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  // GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  // GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  // GPIO_InitStructure.GPIO_Pin = TFT_RD_PIN;
  // GPIO_Init(TFT_RD_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_WR_PIN;
  // GPIO_Init(TFT_WR_PORT, &GPIO_InitStructure);

  //cs a16
  GPIO_InitStructure.GPIO_Pin = TFT_CS_PIN;
  GPIO_Init(TFT_CS_PORT, &GPIO_InitStructure);

  // GPIO_InitStructure.GPIO_Pin = TFT_DC_PIN;
  // GPIO_Init(TFT_DC_PORT, &GPIO_InitStructure);

  /* tft rst引脚*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_Pin = TFT_RST_PIN;
  GPIO_Init(TFT_RST_PORT, &GPIO_InitStructure);

  /* tft 背光*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = TFT_BK_PIN;
  GPIO_Init(TFT_BK_PORT, &GPIO_InitStructure);

  GPIO_PinAFConfig(GPIOD, GPIO_PinSource7, GPIO_AF_FSMC);
}

/**
 * @brief  在TFT显示器上开辟一个窗口
 * @param  usX ：在特定扫描方向下窗口的起点X坐标
 * @param  usY ：在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth ：窗口的宽度
 * @param  usHeight ：窗口的高度
 * @retval 无
 */
void TFT_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{
  //原版：
  // WriteComm(CMD_SetCoordinateX); /* 设置X坐标 */
  // WriteData(usX >> 8);           /* 先高8位，然后低8位 */
  // WriteData(usX & 0xff);         /* 设置起始点和结束点*/
  // WriteData((usX + usWidth - 1) >> 8);
  // WriteData((usX + usWidth - 1) & 0xff);

  // WriteComm(CMD_SetCoordinateY); /* 设置Y坐标*/
  // WriteData(usY >> 8);
  // WriteData(usY & 0xff);
  // WriteData((usY + usHeight - 1) >> 8);
  // WriteData((usY + usHeight - 1) & 0xff);

  //优化版：
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateX); /* 设置X坐标 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usX >> 8);          /* 先高8位，然后低8位 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usX & 0xff);        /* 设置起始点和结束点*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((usX + usWidth - 1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((usX + usWidth - 1) & 0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateY); /* 设置Y坐标*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usY >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usY & 0xff);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((usY + usHeight - 1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((usY + usHeight - 1) & 0xff);
}

/**
 * @brief  设定TFT的光标坐标
 * @param  usX ：在特定扫描方向下光标的X坐标
 * @param  usY ：在特定扫描方向下光标的Y坐标
 * @retval 无
 */
static void TFT_SetCursor(uint16_t usX, uint16_t usY)
{

  TFT_OpenWindow(usX, usY, 1, 1);
}

/**
 * @description: tft屏幕硬件初始化，内部使用
 * @param {*}
 * @return {*}
 */
static void LCD_Rst(void)
{
  GPIO_ResetBits(TFT_RST_PORT, TFT_RST_PIN);

  delay_ms(10);

  GPIO_SetBits(TFT_RST_PORT, TFT_RST_PIN);

  delay_ms(10);
}

/**
  * @brief  从TFT读取数据
  * @param  无
  * @retval 读取到的数据
  */
__inline uint16_t TFT_Read_Data(void)
{
  return (*(__IO uint16_t *)(FSMC_Addr_TFT_DATA));
}
/**
 * @brief  读取TFT GRAN 的一个像素数据
 * @param  无
 * @retval 像素数据
 */
static uint16_t TFT_Read_PixelData(void)
{
  uint16_t usR = 0, usG = 0, usB = 0;

  WriteComm(0x2E); /* 读数据 */
  //原版：
  // usR = TFT_Read_Data(); /*FIRST READ OUT DUMMY DATA*/

  // usR = TFT_Read_Data(); /*READ OUT RED DATA  */
  // usB = TFT_Read_Data(); /*READ OUT BLUE DATA*/
  // usG = TFT_Read_Data(); /*READ OUT GREEN DATA*/

  //优化版
  usR = (*(__IO uint16_t *)(FSMC_Addr_TFT_DATA)); /*FIRST READ OUT DUMMY DATA*/

  usR = (*(__IO uint16_t *)(FSMC_Addr_TFT_DATA)); /*READ OUT RED DATA  */
  usB = (*(__IO uint16_t *)(FSMC_Addr_TFT_DATA)); /*READ OUT BLUE DATA*/
  usG = (*(__IO uint16_t *)(FSMC_Addr_TFT_DATA)); /*READ OUT GREEN DATA*/

  return (((usR >> 11) << 11) | ((usG >> 10) << 5) | (usB >> 11));
}
/**
 * @brief  对TFT显示器的某一点以某种颜色进行填充
 * @param  usX ：在特定扫描方向下该点的X坐标
 * @param  usY ：在特定扫描方向下该点的Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
__inline void TFT_SetPointPixel(uint16_t usX, uint16_t usY)
{
  //原版：
  //if ((usX < LCD_X_LENGTH) && (usY < LCD_Y_LENGTH))
  //{
  //TFT_SetCursor(usX, usY);

  //TFT_FillColor(1, CurrentTextColor);
  //}

  //优化版
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = CMD_SetCoordinateX;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = usX >> 8;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = usX & 0xff;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usX) >> 8;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usX)&0xff;
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = CMD_SetCoordinateY;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = usY >> 8;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = usY & 0xff;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usY) >> 8;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (usY)&0xff;
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = CMD_SetPixel;
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = CurrentTextColor;
}
/**
 * @brief  获取 TFT 显示器上某一个坐标点的像素数据
 * @param  usX ：在特定扫描方向下该点的X坐标
 * @param  usY ：在特定扫描方向下该点的Y坐标
 * @retval 像素数据
 */
uint16_t TFT_GetPointPixel(uint16_t usX, uint16_t usY)
{
  uint16_t usPixelData;

  TFT_SetCursor(usX, usY);

  usPixelData = TFT_Read_PixelData();

  return usPixelData;
}
/**
 * @brief  在 TFT 显示器上使用 Bresenham 算法画线段 
 * @param  usX1 ：在特定扫描方向下线段的一个端点X坐标
 * @param  usY1 ：在特定扫描方向下线段的一个端点Y坐标
 * @param  usX2 ：在特定扫描方向下线段的另一个端点X坐标
 * @param  usY2 ：在特定扫描方向下线段的另一个端点Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
__inline void TFT_DrawLine(uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2)
{
  uint16_t us;
  uint16_t usX_Current, usY_Current;

  int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance;
  int32_t lIncrease_X, lIncrease_Y;

  lDelta_X = usX2 - usX1; //计算坐标增量
  lDelta_Y = usY2 - usY1;

  usX_Current = usX1;
  usY_Current = usY1;

  if (lDelta_X > 0)
    lIncrease_X = 1; //设置单步方向

  else if (lDelta_X == 0)
    lIncrease_X = 0; //垂直线

  else
  {
    lIncrease_X = -1;
    lDelta_X = -lDelta_X;
  }

  if (lDelta_Y > 0)
    lIncrease_Y = 1;

  else if (lDelta_Y == 0)
    lIncrease_Y = 0; //水平线

  else
  {
    lIncrease_Y = -1;
    lDelta_Y = -lDelta_Y;
  }

  if (lDelta_X > lDelta_Y)
    lDistance = lDelta_X; //选取基本增量坐标轴

  else
    lDistance = lDelta_Y;

  for (us = 0; us <= lDistance + 1; us++) //画线输出
  {
    TFT_SetPointPixel(usX_Current, usY_Current); //画点

    lError_X += lDelta_X;
    lError_Y += lDelta_Y;

    if (lError_X > lDistance)
    {
      lError_X -= lDistance;
      usX_Current += lIncrease_X;
    }

    if (lError_Y > lDistance)
    {
      lError_Y -= lDistance;
      usY_Current += lIncrease_Y;
    }
  }
}
/**
 * @brief  在 TFT 显示器上画一个矩形
 * @param  usX_Start ：在特定扫描方向下矩形的起始点X坐标
 * @param  usY_Start ：在特定扫描方向下矩形的起始点Y坐标
 * @param  usWidth：矩形的宽度（单位：像素）
 * @param  usHeight：矩形的高度（单位：像素）
 * @param  ucFilled ：选择是否填充该矩形
  *   该参数为以下值之一：
  *     @arg 0 :空心矩形
  *     @arg 1 :实心矩形 
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void TFT_DrawRectangle(uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled)
{
  if (ucFilled)
  {
    TFT_OpenWindow(usX_Start, usY_Start, usWidth, usHeight);
    TFT_FillColor(usWidth * usHeight, CurrentTextColor);
  }
  else
  {
    TFT_DrawLine(usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start);
    TFT_DrawLine(usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1);
    TFT_DrawLine(usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1);
    TFT_DrawLine(usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1);
  }
}

/**
 * @brief  对TFT显示器的某一窗口以某种颜色进行清屏
 * @param  usX ：在特定扫描方向下窗口的起点X坐标
 * @param  usY ：在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth ：窗口的宽度
 * @param  usHeight ：窗口的高度
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void TFT_Clear(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{
  TFT_OpenWindow(usX, usY, usWidth, usHeight);

  TFT_FillColor(usWidth * usHeight, CurrentBackColor);
}
void TFT_ClearFull(uint16_t color)
{
  CurrentBackColor = color;
  TFT_Clear(0, 0, 480, TFT_MORE_PIXEL);
}
void TFT_GramScan(uint8_t ucOption)
{
  //参数检查，只可输入0-7
  if (ucOption > 7)
    return;

  //根据模式更新LCD_SCAN_MODE的值，主要用于触摸屏选择计算参数
  lcdScanMode = ucOption;

  //根据模式更新XY方向的像素宽度
  if (ucOption % 2 == 0)
  {
    //0 2 4 6模式下X方向像素宽度为480，Y方向为854
    LCD_X_LENGTH = TFT_LESS_PIXEL;
    LCD_Y_LENGTH = TFT_MORE_PIXEL;
  }
  else
  {
    //1 3 5 7模式下X方向像素宽度为854，Y方向为480
    LCD_X_LENGTH = TFT_MORE_PIXEL;
    LCD_Y_LENGTH = TFT_LESS_PIXEL;
  }

  //0x36命令参数的高3位可用于设置GRAM扫描方向
  WriteComm(0x36);
  WriteData(0x00 | (ucOption << 5)); //根据ucOption的值设置LCD参数，共0-7种模式
  WriteComm(CMD_SetCoordinateX);
  WriteData(0x00);                             /* x 起始坐标高8位 */
  WriteData(0x00);                             /* x 起始坐标低8位 */
  WriteData(((LCD_X_LENGTH - 1) >> 8) & 0xFF); /* x 结束坐标高8位 */
  WriteData((LCD_X_LENGTH - 1) & 0xFF);        /* x 结束坐标低8位 */

  WriteComm(CMD_SetCoordinateY);
  WriteData(0x00);                             /* y 起始坐标高8位 */
  WriteData(0x00);                             /* y 起始坐标低8位 */
  WriteData(((LCD_Y_LENGTH - 1) >> 8) & 0xFF); /* y 结束坐标高8位 */
  WriteData((LCD_Y_LENGTH - 1) & 0xFF);        /* y 结束坐标低8位 */

  /* write gram start */
  WriteComm(CMD_SetPixel);
}

/**
 * @description: TFT初始化寄存器配置代码，内部使用
 * @param {*}
 * @return {*}
 */
static void LCD_REG_Config(void)
{

  //   //************* Start Initial Sequence **********//
  WriteComm(0xFF); // EXTC Command Set enable register
  WriteData(0xFF);
  WriteData(0x98);
  WriteData(0x06);

  WriteComm(0xBA); // SPI Interface Setting
  WriteData(0xE0);

  //这里不能改
  WriteComm(0xBC); // GIP 1
  WriteData(0x03);
  WriteData(0x0F);
  WriteData(0x63);
  WriteData(0x69);
  WriteData(0x01);
  WriteData(0x01);
  WriteData(0x1B);
  WriteData(0x11);
  WriteData(0x70);
  WriteData(0x73);
  WriteData(0xFF);
  WriteData(0xFF);
  WriteData(0x08);
  WriteData(0x09);
  WriteData(0x05);
  WriteData(0x00);
  WriteData(0xEE);
  WriteData(0xE2);
  WriteData(0x01);
  WriteData(0x00);
  WriteData(0xC1);

  WriteComm(0xBD); // GIP 2
  WriteData(0x01);
  WriteData(0x23);
  WriteData(0x45);
  WriteData(0x67);
  WriteData(0x01);
  WriteData(0x23);
  WriteData(0x45);
  WriteData(0x67);

  WriteComm(0xBE); // GIP 3
  WriteData(0x00);
  WriteData(0x22);
  WriteData(0x27);
  WriteData(0x6A);
  WriteData(0xBC);
  WriteData(0xD8);
  WriteData(0x92);
  WriteData(0x22);
  WriteData(0x22);

  WriteComm(0xC7); // Vcom
  WriteData(0x1E);

  WriteComm(0xED); // EN_volt_reg
  WriteData(0x7F);
  WriteData(0x0F);
  WriteData(0x00);

  WriteComm(0xC0); // Power Control 1
  WriteData(0xE3);
  WriteData(0x0B);
  WriteData(0x00);

  WriteComm(0xFC);
  WriteData(0x08);

  WriteComm(0xDF); // Engineering Setting
  WriteData(0x00);
  WriteData(0x00);
  WriteData(0x00);
  WriteData(0x00);
  WriteData(0x00);
  WriteData(0x02);

  WriteComm(0xF3); // DVDD Voltage Setting
  WriteData(0x74);

  WriteComm(0xB4); // Display Inversion Control
  WriteData(0x00);
  WriteData(0x00);
  WriteData(0x00);

  WriteComm(0xF7); // 480x854
  WriteData(0x81);

  WriteComm(0xB1); // Frame Rate
  WriteData(0x00);
  WriteData(0x13);
  WriteData(0x13);

  WriteComm(0xF1); // Panel Timing Control
  WriteData(0x29);
  WriteData(0x8A);
  WriteData(0x07);

  WriteComm(0xF2); //Panel Timing Control
  WriteData(0x40);
  WriteData(0xD2);
  WriteData(0x50);
  WriteData(0x28);

  WriteComm(0xC1); // Power Control 2
  WriteData(0x17);
  WriteData(0X85);
  WriteData(0x85);
  WriteData(0x20);

  WriteComm(0xE0);
  WriteData(0x00); //P1
  WriteData(0x0C); //P2
  WriteData(0x15); //P3
  WriteData(0x0D); //P4
  WriteData(0x0F); //P5
  WriteData(0x0C); //P6
  WriteData(0x07); //P7
  WriteData(0x05); //P8
  WriteData(0x07); //P9
  WriteData(0x0B); //P10
  WriteData(0x10); //P11
  WriteData(0x10); //P12
  WriteData(0x0D); //P13
  WriteData(0x17); //P14
  WriteData(0x0F); //P15
  WriteData(0x00); //P16

  WriteComm(0xE1);
  WriteData(0x00); //P1
  WriteData(0x0D); //P2
  WriteData(0x15); //P3
  WriteData(0x0E); //P4
  WriteData(0x10); //P5
  WriteData(0x0D); //P6
  WriteData(0x08); //P7
  WriteData(0x06); //P8
  WriteData(0x07); //P9
  WriteData(0x0C); //P10
  WriteData(0x11); //P11
  WriteData(0x11); //P12
  WriteData(0x0E); //P13
  WriteData(0x17); //P14
  WriteData(0x0F); //P15
  WriteData(0x00); //P16

  WriteComm(0x35); //Tearing Effect ON
  WriteData(0x00);

  WriteComm(0x36);
  WriteData(0x60);

  WriteComm(0x3A);
  WriteData(0x55);

  WriteComm(0x11); //Exit Sleep
  //Delay(320);
  delay_ms(10);
  WriteComm(0x29); // Display On
  //Delay(500);
  delay_ms(10);
}
/**
 * @description:tft屏幕背光控制
 * @param {int} State ：1为开启，0为关闭
 * @return {*}
 */
void TFT_BackLed_Control(int State)
{
  if (!State)
    GPIO_ResetBits(TFT_BK_PORT, TFT_BK_PIN);
  else
    GPIO_SetBits(TFT_BK_PORT, TFT_BK_PIN);
}
/**********************************************
Lcd初始化函数，外部调用
***********************************************/
void Lcd_Initialize(void)
{

  LCD_GPIO_Config();
  LCD_FSMC_Init();

  LCD_Rst();
  LCD_REG_Config();
  //设置默认扫描方向，其中 6 模式为大部分液晶例程的默认显示方向
  TFT_GramScan(lcdScanMode);
  delay_ms(10);
  TFT_BackLed_Control(1);
  LCD_DMA_Init_Core();
  TFT_Clear(0, 0, TFT_LESS_PIXEL, TFT_MORE_PIXEL);
}
/******************************************
函数名：Lcd写命令函数
功能：向Lcd指定位置写入应有命令或数据
入口参数：Index 要寻址的寄存器地址
          ConfigTemp 写入的数据或命令值
******************************************/
static void LCD_WR_REG(u16 Index, u16 CongfigTemp)
{
  WriteComm(Index);
  WriteData(CongfigTemp);
}

/**********************************************
函数名：Lcd块选函数
功能：选定Lcd上指定的矩形区域

注意：xStart、yStart、Xend、Yend随着屏幕的旋转而改变，位置是矩形框的四个角

入口参数：xStart x方向的起始点
          ySrart y方向的起始点
          Xend   y方向的终止点
          Yend   y方向的终止点
返回值：无
***********************************************/
void BlockWrite(unsigned int Xstart, unsigned int Xend, unsigned int Ystart,
                unsigned int Yend)
{

  WriteComm(0x2a);
  WriteData(Xstart >> 8);
  WriteData(Xstart & 0xff);
  WriteData(Xend >> 8);
  WriteData(Xend & 0xff);

  WriteComm(0x2b);
  WriteData(Ystart >> 8);
  WriteData(Ystart & 0xff);
  WriteData(Yend >> 8);
  WriteData(Yend & 0xff);
}

/**
 * @brief : 颜色填充
 * @param {*}
 * @return {*}
 */
void TFT_Clear2(unsigned int x0, unsigned int y0, unsigned int x1,
                unsigned int y1, uint16_t color)
{

  //原版
  // int width = abs(x1 - x0) + 1;
  // int height = abs(y1 - y0) + 1;
  // int x = x1 >= x0 ? x0 : x1;
  // int y = y1 >= y0 ? y0 : y1;
  // int all = width * height;
  // uint32_t i = 0;
  // TFT_OpenWindow(x, y, width, height);
  // //BlockWrite(x0,x1,y0,y1);
  // TFT_FillColor(all, color);

  //优化版：
  int width = abs(x1 - x0) + 1;
  int height = abs(y1 - y0) + 1;
  int x = x1 >= x0 ? x0 : x1;
  int y = y1 >= y0 ? y0 : y1;
  int all = width * height;
  uint32_t i = 0;
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateX); /* 设置X坐标 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x >> 8);            /* 先高8位，然后低8位 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x & 0xff);          /* 设置起始点和结束点*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x + width - 1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x + width - 1) & 0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateY); /* 设置Y坐标*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y & 0xff);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y + height - 1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y + height - 1) & 0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) =
      (CMD_SetPixel);

  for (i = 0; i < all; i++)
    *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (color);
}
/**
  * @brief  设置LCD的背景颜色,RGB565
  * @param  Color: 指定背景颜色 
  * @retval None
  */
void TFT_SetBackColor(uint16_t Color)
{
  CurrentBackColor = Color;
}
/**
  * @brief  设置LCD的前景(字体)颜色,RGB565
  * @param  Color: 指定前景(字体)颜色 
  * @retval None
  */
void TFT_SetTextColor(uint16_t Color)
{
  CurrentTextColor = Color;
}
/**
 * @brief : 绘制图像
 * @param {uint16_t} x
 * @param {uint16_t} y
 * @param {uint16_t} width
 * @param {uint16_t} height
 * @return {*}
 */
__inline void TFT_DrawBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t *pic)
{

  int all = width * height;
  int i = 0;
  // WriteComm(0x36);
  // //0,3,5,6可用
  // WriteData(0x00 | (1 << 5)); //根据ucOption的值设置LCD参数，共0-7种模式
  //TFT_OpenWindow(x, y, width, height);
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateX); /* 设置X坐标 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x >> 8);            /* 先高8位，然后低8位 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x & 0xff);          /* 设置起始点和结束点*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x + width - 1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x + width - 1) & 0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateY); /* 设置Y坐标*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y & 0xff);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y + height - 1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y + height - 1) & 0xff);
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetPixel);
  for (i = 0; i < all; i++)
  {
    *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = pic[i];
  }
}
/**
 * @brief : 绘制bitmap
 * @param {uint16_t} x0 
 * @param {uint16_t} y0
 * @param {uint16_t} x1
 * @param {uint16_t} y1
 * @param {uint16_t} *pic bitmap颜色
 * @param {uint8_t} direction 暂无作用
 * @return {*}
 */
__inline void TFT_DrawBitmap2(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t *pic, uint8_t direction)
{
  int i;
  int width = abs(x1 - x0) + 1;
  int height = abs(y1 - y0) + 1;
  // int x = x1 >= x0 ? x0 : x1;
  // int y = y1 >= y0 ? y0 : y1;
  int all = width * height;

  //TFT_OpenWindow(x, y, width, height);
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateX); /* 设置X坐标 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x0 >> 8);           /* 先高8位，然后低8位 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x0 & 0xff);         /* 设置起始点和结束点*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x1)&0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateY); /* 设置Y坐标*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y0 >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y0 & 0xff);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y1)&0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetPixel);
  //这里还可以优化，使用DMA传输会快一些，且不占用cpu资源
  for (i = 0; i < all; i++)
  {
    *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = pic[i];
  }
}

#ifdef GUI_LVGL

/**
 * Flush a buffer to the marked area
 * @param drv pointer to driver where this function belongs
 * @param area an area where to copy `color_p`
 * @param color_p an array of pixel to copy to the `area` part of the screen
 */
__inline static void monitor_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
  lv_coord_t hres = disp_drv->hor_res;
  lv_coord_t vres = disp_drv->ver_res;

  //    printf("x1:%d,y1:%d,x2:%d,y2:%d\n", area->x1, area->y1, area->x2, area->y2);

  /*Return if the area is out the screen*/
  if (area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1)
  {
    lv_disp_flush_ready(disp_drv);
    return;
  }

#if MONITOR_DOUBLE_BUFFERED
  // monitor.tft_fb_act = (uint32_t *)color_p;
#else                                            /*MONITOR_DOUBLE_BUFFERED*/

  // int32_t y;
#if LV_COLOR_DEPTH != 24 && LV_COLOR_DEPTH != 32 /*32 is valid but support 24 for backward compatibility too*/
  // int32_t x;
  //TFT_DrawBitmap2(area->x1, area->y1, area->x2, area->y2, &color_p->full, 0);
  LCD_Start_DMA_Fill(area->x1, area->y1, area->x2, area->y2, &color_p->full);
  // for (y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++)
  // {
  //   for (x = area->x1; x <= area->x2; x++)
  //   {
  //     //monitor.tft_fb[y * disp_drv->hor_res + x] = lv_color_to32(*color_p);
  //     TFT_SetTextColor(color_p->full);
  //     TFT_SetPointPixel(x, y);
  //     color_p++;
  //   }
  // }
#else
  uint32_t w = lv_area_get_width(area);
  for (y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++)
  {
    memcpy(&monitor.tft_fb[y * MONITOR_HOR_RES + area->x1], color_p, w * sizeof(lv_color_t));
    color_p += w;
  }
#endif
#endif /*MONITOR_DOUBLE_BUFFERED*/

  /*IMPORTANT! It must be called to tell the system the flush is ready*/
  //lv_disp_flush_ready(disp_drv);
}
static int touchX, touchY;
/*Return true is the touchpad is pressed*/
static uint8_t touchpad_is_pressed(void)
{
  getCoord(&touchX, &touchY);
  /*Your code comes here*/
  if (touchX != 0xffff && touchY != 0xffff)
  {
    return 1;
  }
  return 0;
}

/*Get the x and y coordinates if the touchpad is pressed*/
/**
 * @description: 
 * @param {lv_coord_t} *x
 * @param {lv_coord_t} *y
 * @return {*}
 */
static void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y)
{
  /*Your code comes here*/
#if (LCD_SCAN_MODE == 6)
  (*x) = touchX;
  (*y) = 856 - touchY;
#elif (LCD_SCAN_MODE == 5)
  (*x) = 850 - touchY;
  (*y) = 498 - touchX;

#endif
}
/*Will be called by the library to read the touchpad*/
/**
 * @description: 
 * @param {lv_indev_drv_t} *indev_drv
 * @param {lv_indev_data_t} *data
 * @return {*}
 */
static void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
  static lv_coord_t last_x = 0;
  static lv_coord_t last_y = 0;
  //printf("touch\n");
  /*Save the pressed coordinates and the state*/
  if (touchpad_is_pressed())
  {
    touchpad_get_xy(&last_x, &last_y);
    data->state = LV_INDEV_STATE_PR;
  }
  else
  {
    data->state = LV_INDEV_STATE_REL;
  }

  /*Set the last pressed coordinates*/
  data->point.x = last_x;
  data->point.y = last_y;
}

/**
 * @brief :
 * @param {_lv_fs_drv_t} *drv
 * @return {*}
 */
bool my_ready_cb(struct _lv_fs_drv_t *drv)
{
  return 1;
}
/**
 * @brief :
 * @param {lv_fs_drv_t} *drv
 * @param {char} *path
 * @param {lv_fs_mode_t} mode
 * @return {*}
 */
void *my_open_cb(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode)
{
  FIL *fp = rt_malloc(sizeof(FIL));
  if (fp == NULL)
  {
    return NULL;
  }
  lv_fs_res_t r = LV_FS_RES_NOT_IMP;
  if (mode == LV_FS_MODE_WR)
  {
    r = f_open(fp, path, FA_WRITE);
  }
  else if (mode == LV_FS_MODE_RD)
  {
    r = f_open(fp, path, FA_READ);
  }
  else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
  {
    /*Open a file for read and write*/
    r = f_open(fp, path, FA_READ | FA_WRITE);
  }
  if (r == FR_OK)
    return fp;
  else
    return NULL;
}
/**
 * @brief :
 * @param {_lv_fs_drv_t} *drv
 * @param {void} *file_p
 * @return {*}
 */
lv_fs_res_t my_close_cb(struct _lv_fs_drv_t *drv, void *file_p)
{
  lv_fs_res_t r = LV_FS_RES_NOT_IMP;
  r = f_close((FIL *)file_p);
  rt_free((FIL *)file_p);
  if (r == FR_OK)
    r = LV_FS_RES_OK;
  return r;
}
/**
 * Read data from an opened file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable.
 * @param buf       pointer to a memory block where to store the read data
 * @param btr       number of Bytes To Read
 * @param br        the real number of read bytes (Byte Read)
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
lv_fs_res_t my_read_cb(struct _lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br)
{
  lv_fs_res_t res = LV_FS_RES_NOT_IMP;

  /*Add your code here*/
  if (f_read((FIL *)file_p, buf, btr, br) == FR_OK)
  {
    res = LV_FS_RES_OK;
  }

  return res;
}
/**
 * Write into a file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable
 * @param buf       pointer to a buffer with the bytes to write
 * @param btr       Bytes To Write
 * @param br        the number of real written bytes (Bytes Written). NULL if unused.
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
lv_fs_res_t my_write_cb(struct _lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw)
{
  lv_fs_res_t res = LV_FS_RES_NOT_IMP;

  /*Add your code here*/
  if (f_write((FIL *)file_p, buf, btw, bw) == FR_OK)
  {
    res = LV_FS_RES_OK;
  }
  return res;
}
/**
 * Set the read write pointer. Also expand the file size if necessary.
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable. (opened with lv_ufs_open )
 * @param pos       the new position of read write pointer
 * @param whence    tells from where to interpret the `pos`. See @lv_fs_whence_t
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t my_seek_cb(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence)
{
  lv_fs_res_t res = LV_FS_RES_INV_PARAM;

  /*Add your code here*/
  if (whence == LV_FS_SEEK_SET)
  {
    if (f_lseek((FIL *)file_p, pos) == FR_OK)
    {
      res = LV_FS_RES_OK;
    }
  }

  return res;
}
/**
 * Give the position of the read write pointer
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable.
 * @param pos_p     pointer to to store the result
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t my_tell_cb(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p)
{
  lv_fs_res_t res = LV_FS_RES_NOT_IMP;

  /*Add your code here*/
  *pos_p = f_tell((FIL *)file_p);
  return LV_FS_RES_OK;
}
/**
 * Initialize a 'lv_fs_dir_t' variable for directory reading
 * @param drv       pointer to a driver where this function belongs
 * @param path      path to a directory
 * @return          pointer to the directory read descriptor or NULL on error
 */
static void *my_dir_open_cb(struct _lv_fs_drv_t *drv, const char *path)
{
  //为目录头开辟空间，在调用close函数时会释放，所以务必在打开完后关闭文件夹
  DIR *dir = rt_malloc(sizeof(DIR));
  if (dir == NULL)
  {
    return NULL;
  }
  /*Add your code here*/
  //dir = ... /*Add your code here*/
  if (f_opendir((DIR *)dir, path) == FR_OK)
  {
    return dir;
  }
  else
  {
    return NULL;
  }
}

/**
 * Read the next filename form a directory.
 * The name of the directories will begin with '/'
 * @param drv       pointer to a driver where this function belongs
 * @param rddir_p   目录指针
 * @param fn        pointer to a buffer to store the filename
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t my_dir_read_cb(lv_fs_drv_t *drv, void *rddir_p, char *fn)
{
  lv_fs_res_t res = LV_FS_RES_NOT_IMP;
  //为文件信息开辟内存空间，之后需要释放
  FILINFO *f = rt_malloc(sizeof(FILINFO));

#if _USE_LFN
  f->lfsize = _MAX_LFN * 2 + 1;
  f->lfname = rt_malloc(f->lfsize);
#endif

  if (f == NULL || f->lfname == NULL)
  {
    return LV_FS_RES_OUT_OF_MEM;
  }

  if (f_readdir((DIR *)(rddir_p), f) == FR_OK)
  {
    strcpy(fn, *(f->lfname) ? f->lfname : f->fname);
    res = LV_FS_RES_OK;
  }
  rt_free(f->lfname);
  rt_free(f);

  return res;
}

/**
 * Close the directory reading
 * @param drv       pointer to a driver where this function belongs
 * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t my_dir_close_cb(lv_fs_drv_t *drv, void *rddir_p)
{
  lv_fs_res_t res = LV_FS_RES_NOT_IMP;

  /*Add your code here*/
  if (f_closedir((DIR *)(rddir_p)) == FR_OK)
  {
    rt_free((DIR *)(rddir_p));
    res = LV_FS_RES_OK;
  }
  else
  {
    res = LV_FS_RES_FS_ERR;
  }
  return res;
}
/**
 * @brief :lvgl自带的文件接口初始化，用于对接fatfs
 * @param {*}
 * @return {*}
 */
void fileSystemOpr()
{
  static lv_fs_drv_t drv; /*Needs to be static or global*/
  lv_fs_drv_init(&drv);   /*Basic initialization*/

  drv.letter = '0';           /*An uppercase letter to identify the drive */
  drv.ready_cb = my_ready_cb; /*Callback to tell if the drive is ready to use */
  drv.open_cb = my_open_cb;   /*Callback to open a file */
  drv.close_cb = my_close_cb; /*Callback to close a file */
  drv.read_cb = my_read_cb;   /*Callback to read a file */
  drv.write_cb = my_write_cb; /*Callback to write a file */
  drv.seek_cb = my_seek_cb;   /*Callback to seek in a file (Move cursor) */
  drv.tell_cb = my_tell_cb;   /*Callback to tell the cursor position  */

  drv.dir_open_cb = my_dir_open_cb;   /*Callback to open directory to read its content */
  drv.dir_read_cb = my_dir_read_cb;   /*Callback to read a directory's content */
  drv.dir_close_cb = my_dir_close_cb; /*Callback to close a directory */

  lv_fs_drv_register(&drv); /*Finally register the drive*/
}

static uint8_t __g_font_buf[180]; //如bin文件存在SPI FLASH可使用此buff

static uint8_t *__user_font_getdata(int offset, int size)
{
  extern _font_info ftinfo;
  W25QXX_Read(__g_font_buf, ftinfo.lvglFontAddr + offset, size);
  return __g_font_buf;
}

static const uint8_t *__user_font_get_bitmap(const lv_font_t *font, uint32_t unicode_letter)
{
  if (unicode_letter > __g_xbf_hd.max || unicode_letter < __g_xbf_hd.min)
  {
    return NULL;
  }
  uint32_t unicode_offset = sizeof(x_header_t) + (unicode_letter - __g_xbf_hd.min) * 4;
  uint32_t *p_pos = (uint32_t *)__user_font_getdata(unicode_offset, 4);
  if (p_pos[0] != 0)
  {
    uint32_t pos = p_pos[0];
    glyph_dsc_t *gdsc = (glyph_dsc_t *)__user_font_getdata(pos, sizeof(glyph_dsc_t));
    return __user_font_getdata(pos + sizeof(glyph_dsc_t), gdsc->box_w * gdsc->box_h * __g_xbf_hd.bpp / 8);
  }
  return NULL;
}

static bool __user_font_get_glyph_dsc(const lv_font_t *font, lv_font_glyph_dsc_t *dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
{
  if (unicode_letter > __g_xbf_hd.max || unicode_letter < __g_xbf_hd.min)
  {
    return NULL;
  }
  uint32_t unicode_offset = sizeof(x_header_t) + (unicode_letter - __g_xbf_hd.min) * 4;
  uint32_t *p_pos = (uint32_t *)__user_font_getdata(unicode_offset, 4);
  if (p_pos[0] != 0)
  {
    glyph_dsc_t *gdsc = (glyph_dsc_t *)__user_font_getdata(p_pos[0], sizeof(glyph_dsc_t));
    dsc_out->adv_w = gdsc->adv_w;
    dsc_out->box_h = gdsc->box_h;
    dsc_out->box_w = gdsc->box_w;
    dsc_out->ofs_x = gdsc->ofs_x;
    dsc_out->ofs_y = gdsc->ofs_y;
    dsc_out->bpp = __g_xbf_hd.bpp;
    return true;
  }
  return false;
}

/**
 * @description: lvgl相关的初始化函数
 * @param {*}
 * @return {*}
 */
void hal_init(void)
{
  /*Create a display buffer*/
  static lv_disp_draw_buf_t disp_buf1;
#if GRAM_POSITION == INSIDE_CHIP
  static lv_color_t buf1_1[LVGL_BUFFER_SIZE];

#if DOUBLE_BUFFER == 0

  lv_disp_draw_buf_init(&disp_buf1, buf1_1, NULL, LVGL_BUFFER_SIZE);

#else
  static lv_color_t buf1_2[LVGL_BUFFER_SIZE];
  lv_disp_draw_buf_init(&disp_buf1, buf1_1, buf1_2, LVGL_BUFFER_SIZE);

#endif

#else
  static lv_color_t buf1_1[LVGL_BUFFER_SIZE] __attribute__((at(0x68000000 + RT_HEAP_SIZE * 4)));

#if DOUBLE_BUFFER == 0
  lv_disp_draw_buf_init(&disp_buf1, buf1_1, NULL, LVGL_BUFFER_SIZE);

#else
  static lv_color_t buf1_2[LVGL_BUFFER_SIZE] __attribute__((at(0x68000000 + RT_HEAP_SIZE * 4 + sizeof(buf1_1))));
  lv_disp_draw_buf_init(&disp_buf1, buf1_1, buf1_2, LVGL_BUFFER_SIZE);

#endif

#endif

  /*Create a display*/
  lv_disp_drv_init(&disp_drv); /*Basic initialization*/
  disp_drv.draw_buf = &disp_buf1;
  disp_drv.flush_cb = monitor_flush;
#if (LCD_SCAN_MODE == 6)
  {
    disp_drv.hor_res = MONITOR_VER_RES;
    disp_drv.ver_res = MONITOR_HOR_RES;
  }
#elif (LCD_SCAN_MODE == 5)
  {
    disp_drv.hor_res = MONITOR_HOR_RES;
    disp_drv.ver_res = MONITOR_VER_RES;
  }
#endif
  disp_drv.antialiasing = 1;

  static lv_indev_drv_t indev_drv;
  static lv_indev_t *indev_touchpad;

  /*Register a touchpad input device*/
  lv_indev_drv_init(&indev_drv);
  indev_drv.type = LV_INDEV_TYPE_POINTER;
  indev_drv.read_cb = touchpad_read;
  lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
  //lv_disp_set_rotation(disp, LV_DISP_ROT_90);
  indev_touchpad = lv_indev_drv_register(&indev_drv);

  lv_theme_t *th = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);
  lv_disp_set_theme(disp, th);

  lv_group_t *g = lv_group_create();
  lv_group_set_default(g);

  fileSystemOpr();
}
/**
 * @brief :LCD DMA初始化
 * @param {*}
 * @return {*}
 */
static void LCD_DMA_Init_Core(void)
{
  DMA_InitTypeDef DMA_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  if ((uint32_t)LCD_DMA_Stream > (uint32_t)DMA2) //得到当前stream是属于DMA2还是DMA1
  {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //DMA2时钟使能
  }
  else
  {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能
  }
  DMA_DeInit(LCD_DMA_Stream);

  while (DMA_GetCmdStatus(LCD_DMA_Stream) != DISABLE)
  {
  } //等待DMA可配置

  /* 配置 DMA Stream */
  DMA_InitStructure.DMA_Channel = LCD_DMA_Channel;                        //通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)0;                 //DMA外设地址(后续赋值)
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&LCD_INFO->LCD_RAM;   //DMA 存储器0地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;                     //存储器到外设模式
  DMA_InitStructure.DMA_BufferSize = 0;                                   //数据传输量(后续赋值)
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;         //外设增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;                //存储器非增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord; //外设数据长度:8位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;     //存储器数据长度:8位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 使用普通模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;                     //中等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;         //存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
  DMA_Init(LCD_DMA_Stream, &DMA_InitStructure);                       //初始化DMA Stream

  DMA_ITConfig(LCD_DMA_Stream, DMA_IT_TC, ENABLE);
  //DMA NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  DMA_Cmd(LCD_DMA_Stream, DISABLE);
}
uint16_t *LVGL_Color_Ptr;
/**
 * @brief :LCD DMA填充函数，用于加速LVGL填充速度
 * @param {uint16_t} x1
 * @param {uint16_t} y1
 * @param {uint16_t} x2
 * @param {uint16_t} y2
 * @param {uint16_t} *color
 * @return {*}
 */
static void LCD_Start_DMA_Fill(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t *color)
{
  int width = abs(x1 - x0) + 1;
  int height = abs(y1 - y0) + 1;
  // int x = x1 >= x0 ? x0 : x1;
  // int y = y1 >= y0 ? y0 : y1;
  //像素总数
  int DMA_TransCnt = width * height;
  LVGL_Color_Ptr = color;
  //TFT_OpenWindow(x, y, width, height);
  //以下为上条函数的直接展开，用于加快执行速度
  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateX); /* 设置X坐标 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x0 >> 8);           /* 先高8位，然后低8位 */
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (x0 & 0xff);         /* 设置起始点和结束点*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((x1)&0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetCoordinateY); /* 设置Y坐标*/
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y0 >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = (y0 & 0xff);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y1) >> 8);
  *(__IO uint16_t *)(FSMC_Addr_TFT_DATA) = ((y1)&0xff);

  *(__IO uint16_t *)(FSMC_Addr_TFT_CMD) = (CMD_SetPixel);

  //DMA_Cmd(LCD_DMA_Stream, DISABLE); //关闭DMA传输
  LCD_DMA_Stream->CR &= ~(uint32_t)DMA_SxCR_EN;

  // while (DMA_GetCmdStatus(LCD_DMA_Stream) != DISABLE)
  // {
  // }
  while ((LCD_DMA_Stream->CR & (uint32_t)DMA_SxCR_EN) != 0)
  {
  }

  //要传输的数据总量，不能超过65534,否则使用多次传输
  DMA_TotalCnt = DMA_TransCnt * sizeof(uint16_t);
  DMA_Trans_time = DMA_TotalCnt / 65534 + 1;
  if (DMA_TotalCnt > 65534)
  {
    //printf("over65534 is%d,time:%d\n", DMA_TotalCnt, DMA_Trans_time);
  } //等待DMA可配置

  LCD_DMA_Stream->NDTR = (uint32_t)(DMA_Trans_time > 1 ? 65534 : DMA_TotalCnt); //数据传输量
  LCD_DMA_Stream->PAR = (uint32_t)color;                                        //传输数组
  //DMA_Cmd(LCD_DMA_Stream, ENABLE);                           //开启DMA传输
  LCD_DMA_Stream->CR |= (uint32_t)DMA_SxCR_EN;
}
/**
 * @brief :LCD DMA中断处理
 * @param {*}
 * @return {*}
 */
void DMA2_Stream3_IRQHandler(void)
{
  rt_interrupt_enter();
  uint32_t leftDataCnt;
  if (DMA_GetITStatus(LCD_DMA_Stream, LCD_DMA_IT_TCIFx) != RESET)
  {
    DMA_ClearITPendingBit(LCD_DMA_Stream, LCD_DMA_IT_TCIFx);

    if (DMA_Trans_time > 1)
    {
      DMA_Trans_time--;
      leftDataCnt = DMA_TotalCnt - 65534;
      DMA_TotalCnt -= 65534;
      LVGL_Color_Ptr += 32767;
      LCD_DMA_Stream->NDTR = (uint32_t)(leftDataCnt > 65534 ? 65534 : leftDataCnt); //数据传输量
      LCD_DMA_Stream->PAR = (uint32_t)(LVGL_Color_Ptr);
      //传输数组
      //DMA_Cmd(LCD_DMA_Stream, ENABLE);                           //开启DMA传输
      LCD_DMA_Stream->CR |= (uint32_t)DMA_SxCR_EN;
    }
    else
    {
      lv_disp_flush_ready(&disp_drv);
    }
  }

  rt_interrupt_leave();
}

#endif

#endif